﻿#include "Generation/Content/Reachability.h"

#include "Generation/Engine/Stage/Stage.h"
// #include "Generation/Utilities/BucketQueue.h"

FReachability::FReachability(FStage* stage)
{
	this->stage = stage;
	m_Distances = new FArray2D<int>(stage->Width(), stage->Height(), m_Unknown);
	m_Affected = new FVecSet(stage->Width(), stage->Height());
}

void FReachability::SetDistance(FVec pos, int distance)
{
	// If we're on an open tile, update the running count.
	if (stage->Get(&pos)->type == FTileType::solid)
	{
		if (m_Distances->Get(&pos) >= 0) m_ReachedOpenCount--; // 每次到达一个已经设置过距离的可行点，就减一
		if (distance >= 0) m_ReachedOpenCount++; // 每次到达一个可行点，并且想要设正距离，加一
	}

	m_Distances->Set(&pos, distance); // 设置这个点的距离
}

void FReachability::ClearDistance(bool force) const
{
	for (auto pos : *stage->Bounds())
	{
		if (force)
		{
			m_Distances->Set(&pos, m_Unknown);
		}
		else
		{
			if (m_Distances->Get(&pos) > 0)
			{
				m_Distances->Set(&pos, m_Unknown);
			}
		}
	}
}

void FReachability::Process(TArray<FVec>* starting)
{
	// 新建一个桶队列
	// 遍历每个传入起始点的distance距离，作为cost加入桶队列
	auto frontier = new BucketQueue<FVec>();
	// for (int i = 0; i < m_Distances->Count(); ++i)
	// {
	// 	UE_LOG(LogTD, Error, TEXT("distance: %d"), m_Distances->Get(i));
	// }

	for (auto pos : *starting)
	{
		auto cost = m_Distances->Get(&pos);
		UE_LOG(LogTD, Error, TEXT("start pos cost: %d, %d %d"), cost, pos.x, pos.y);
		frontier->Add(pos, cost);
	}

	// fill-flow
	// 桶队列填充好后，一直尝试从该队列中获取元素，一直到获取不到元素为止
	while (true)
	{
		// auto pos = frontier->RemoveNext();// 获取那些起始点中最近距离的点
		auto pos = frontier->RemoveNext(); // 获取那些起始点中最近距离的点

		if (pos == nullptr) break; // 过头了，所有的都已遍历结束，跳出

		auto parentDistance = m_Distances->Get(pos);

		for (auto here : pos->cardinalNeighbors())
		{
			if (!m_Distances->bounds->Contains(here)) continue; //越过矩形区域

			// Ignore tiles we've already reached.
			if (m_Distances->Get(&here) != m_Unknown) continue; //已经处理过

			//IsWalkable
			auto tileType = stage->Get(&here)->type;
			if (tileType != FTileType::solid &&
				tileType != FTileType::solidWet)
			{
				UE_LOG(LogTD, Error, TEXT("可行走，距离增加1，且自己就是新的起点"));
				auto distance = parentDistance + 1;
				SetDistance(here, distance);
				frontier->Add(here, distance);
			}
			else
			{
				UE_LOG(LogTD, Error, TEXT("不可行走"));
				SetDistance(here, m_Unreachable);
			}
		}
	}
}

FReachability::FillStep::FillStep(FVec pos, int distance)
{
	this->pos.x = pos.x;
	this->pos.y = pos.y;
	this->distance = distance;
}
